#引入
import numpy as np
import cv2


from keras.models import Sequential#此处经过修改，发现并没有keras.emotion_models的库，通过查阅keras官方文档，发现这个引用是合法的
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D
from keras.optimizers import Adam
from keras.layers import MaxPooling2D
from keras.preprocessing.image import ImageDataGenerator

#初始化训练和验证生成器
train_dir = 'E:/学习编程/data/train'#先前的是data/train,通过查阅flow_from_directory函数的用法，了解到这个字符串是调用文件，从而改了
val_dir = 'E:/学习编程/data/test'
train_datagen = ImageDataGenerator(rescale=1./255)
val_datagen = ImageDataGenerator(rescale=1./255)
train_generator=train_datagen.flow_from_directory(
        train_dir,
        target_size=(48,48), 
        batch_size=64,
        color_mode="grayscale",
        #这个颜色开始时是gray_framescale，然后编译器就开始报错，u1s1,这个问题我还是花了点时间的，刚开始时我选择的是编译器推荐的第一个rbg,然后编译器还是在这报错，然后我又去查了这个函数的一些参数
        #给这个函数填了很多不必要的东西，虽然编译器不在这个地方报错了，但是终端出现了很多奇怪的东西，在后面函数报错时，几次尝试无果后，又开始看前面问题，最后在网上查了一些资料（刚开始还以为几个颜色没有什么区别
        #rbg是彩色的，而grayscale是灰度化，和原文的意思最为贴切，然后改了，而且根据在后面查的资料，灰度化可以加快获取的速度
        class_mode='categorical',
       
        )

validation_generator = val_datagen.flow_from_directory(
        val_dir,
        target_size=(48,48),
        batch_size=64,
        color_mode="grayscale",#这个也是同上
        class_mode='categorical',
       
        )

#生成卷积神经网络
emotion_model = Sequential()

emotion_model.add(Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=(48,48,1)))
emotion_model.add(Conv2D(64, kernel_size=(3, 3), activation='relu'))
emotion_model.add(MaxPooling2D(pool_size=(2, 2)))
emotion_model.add(Dropout(0.25))

emotion_model.add(Conv2D(128, kernel_size=(3, 3), activation='relu'))
emotion_model.add(MaxPooling2D(pool_size=(2, 2)))
emotion_model.add(Conv2D(128, kernel_size=(3, 3), activation='relu'))
emotion_model.add(MaxPooling2D(pool_size=(2, 2)))
emotion_model.add(Dropout(0.25))

emotion_model.add(Flatten())
emotion_model.add(Dense(1024, activation='relu'))
emotion_model.add(Dropout(0.5))
emotion_model.add(Dense(7, activation='softmax'))

#编译和训练模型
emotion_model.compile(loss='categorical_crossentropy',optimizer=Adam(lr=0.0001, decay=1e-6),metrics=['accuracy'])

emotion_model_info = emotion_model.fit_generator(
    #当时在初步调时，这个地方开始报错，调了很多遍都没有调好（当时调成了fit），后来转回去调flow_from_directory时，这个又好了，后来要在网上看这个是怎么运行时，发现网上普遍用fit_generator,我开始对代码产生怀疑，后来发现终端给了我这样的指令
    # `Model.fit_generator` is deprecated and will be removed in a future version. Please use `Model.fit`, which supports generators.这才解开我的疑问
        train_generator,
        steps_per_epoch=28709 // 64,
        epochs=1,#轮回50次太多了，我的电脑运行一次要很长时间，所以改了
        validation_data=validation_generator,
        validation_steps=7178 // 64)

#保存模型权重
emotion_model.save_weights('model.h5')

#利用openCV haarcascade xml检测摄像头中人脸的边界框并预测情绪:
cv2.ocl.setUseOpenCL(False)

emotion_dict = {0: "Angry", 1: "Disgusted", 2: "Fearful", 3: "Happy", 4: "Neutral", 5: "Sad", 6: "Surprised"}

cap = cv2.VideoCapture(0)
while True:
    ret, frame = cap.read()
    if not ret:
        break
    bounding_box = cv2.CascadeClassifier('E:\\haarcascade_frontalface_default.xml')
    #这个问题是我卡的时间最多的，卡了好几天，一度想要放弃，现在我就说说我怎么搞好的吧。解决完前面的问题时，再次运行发现编译器在91行报错，然后一直在网上查有关detectMultiScale函数的资料和信息，
    #不断地改函数，加参数。持续了很长时间一直无果。然后，突然灵机一动，把报错的部分复制到CSDN中，结果真的得到了满意的结果，但是这并不代表结束。在发现是我的文件地址导入错误后，我按照cmd上的地址
    #想找回去，结果，我一直找不到appdata这个文件夹，然后又郁闷了很久，在我的电脑中直接搜索一直找不到，后来在Windows10下栏的搜索中找（毕竟速度快一点），再输入appdata后，突然出现appdata找不到的搜索
    #我立刻打开网页，了解到他是隐藏文件后，好像99步已经走了100步，但是当我把文件地址复制上去时还是不行，我后来尝试了很多方法，改大小写等等，最后我意识到可能是我的文件地址太长了，然后我直接把文件复制到
    #E盘，然后就成了
    gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)#解决上一个问题后，编译器就给我报错了后来在网上查资料，经过一些筛选后，因为灰度选择了这个
    num_faces = bounding_box.detectMultiScale(gray_frame, scaleFactor=1.3, minNeighbors=5)

    for (x, y, w, h) in num_faces:
        cv2.rectangle(frame, (x, y-50), (x+w, y+h+10), (255, 0, 0), 2) 
        roi_gray_frame = gray_frame[y:y + h, x:x + w]
        cropped_img = np.expand_dims(np.expand_dims(cv2.resize(roi_gray_frame, (48, 48)), -1), 0)
        emotion_prediction = emotion_model.predict(cropped_img)
        maxindex = int(np.argmax(emotion_prediction))
        cv2.putText(frame, emotion_dict[maxindex], (x+20, y-60), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)

    cv2.imshow('Video', cv2.resize(frame,(1200,860),interpolation = cv2.INTER_CUBIC))
    if cv2.waitKey(1) & 0xFF == ord('q'):
        cap.release()
        #这俩个修改就很简单了，一放到编译器就给我报错了，把其放在if模块中
        cv2.destroyAllWindows()
    break
